/**
 * @(#)NewTaskServiceImpl.java 2016年11月3日
 * Copyright(C) 2016 ZHEXIN IT CO.,LTD. All rights reserved.
 */
package org.jsbd.boss.service.impl;

import org.apache.commons.lang.StringUtils;
import org.jsbd.boss.common.redis.RedisClient;
import org.jsbd.boss.domian.TaskVo;
import org.jsbd.boss.domian.channel.ApkLoginTypeConfig;
import org.jsbd.boss.domian.channel.ChannelApkConfig;
import org.jsbd.boss.domian.channel.ThirdUser;
import org.jsbd.boss.domian.channel.UserClientInfo;
import org.jsbd.boss.enums.ApkBaseEnum;
import org.jsbd.boss.enums.TaskStatusEnum;
import org.jsbd.boss.enums.TaskTypeEnum;
import org.jsbd.boss.service.*;
import org.jsbd.boss.task.OssTask;
import org.jsbd.boss.util.BeanCopierUtil;
import org.jsbd.boss.util.CacheKey;
import org.jsbd.boss.util.DateUtil;
import org.jsbd.boss.util.VersonCompareUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;


/**
 * 新增用户任务
 *
 * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
 * @version 1.0.0
 */
@Service("newTaskService")
public class NewTaskServiceImpl implements INewTaskService {
    public static final Logger LOGGER = LoggerFactory.getLogger(NewTaskServiceImpl.class);
    @Resource
    private RedisClient<String, String> redisClient;
    @Resource
    private SimulatorManager simulatorManager;
    @Resource
    private IChannelLimitService channelLimitService;

    @Resource
    private ApkManager apkManager;

    @Resource
    private IChannelApkInfoService channelApkInfoService;

    @Resource
    private TaskLuaManager taskLuaManager;

    @Resource
    private ISimulatorLogService simulatorLogService;

    @Resource
    private IApkSimulatorService apkSimulatorService;

    @Resource
    private IScriptService scriptService;

    @Resource
    private CacheParamManager cacheParamManager;


    @Resource
    private ITaskPoolService taskPoolService;

    @Resource
    private IUserClientInfoLogService userClientInfoLogService;


    /*
     * (non-Javadoc)
     *
     * @see org.jsbd.boss.task.TaskService#addTask(org.jsbd.boss.task.vo.TaskVo)
     */
    @Override
    public TaskStatusEnum addTask(TaskVo taskVo) throws Exception {
        TaskStatusEnum status = TaskStatusEnum.FAIL;
        // 判断当前队列长度在范围内，并且更新用户登录标记
        String mark = taskVo.getMark();
        String sysVer = apkSimulatorService.getVer(mark);
        taskVo.setSysVer(sysVer);
        int size = simulatorManager.getVerCanUseSimulator(sysVer);
        String imsi = taskVo.getImsi();
        if (size > 0) {
            Integer apkId = taskVo.getApkId();
            Integer base = taskVo.getBase();
            // 所有城市默认为-1
            taskVo.setCity(-1);
            String hasLoginKey = null;
            // 用户当天任务标记
            String lockKey = null;
            if (base == null || base == ApkBaseEnum.OTHER.getType()) {
                hasLoginKey = apkId + "|" + System.currentTimeMillis() + "|";
                lockKey = imsi + apkId;
            } else {
                hasLoginKey = base + "|" + System.currentTimeMillis() + "|";
                lockKey = imsi + base;
            }
            //指定文件脚本
            String script = scriptService.randomScript(mark, TaskTypeEnum.ADD_TASK.getType());
            if (StringUtils.isNotBlank(script)) {
                taskVo.setAddScriptPath(script);
            }
            String queueKey = CacheKey.VER_TASK_QUEUE + sysVer;
            boolean isNewSimulator = checkNewVersion(sysVer);
            taskVo.setNewUser(isNewSimulator);
            if (isNewSimulator) {
                queueKey = CacheKey.NEW_SDK_QUEUE + sysVer;
                taskVo.setAppName(null);
                taskVo.setSystemAppName(null);
            }
            String task = TaskUtil.initTaskJSON(taskVo).toJSONString();
            long daysOfTime = DateUtil.getDaysOfTime();
            String[] args = new String[]{queueKey,
                    task,
                    CacheKey.A_IMSI + imsi,
                    String.valueOf(size),
                    hasLoginKey,
                    initTaskRecordKey(mark),
                    lockKey,
                    CacheKey.APK_LOGIN_KEY + daysOfTime + mark,
                    taskVo.getLoginType() + "total",
                    taskVo.getLoginType() + "exectotal",
                    };

            Long result = (Long) redisClient.eval(getAddTaskLua(), ReturnType.VALUE, args);
            if (result.intValue() == TaskStatusEnum.SUCCESS.getType()) {
                status = TaskStatusEnum.SUCCESS;
                //生成任务文件 新版本不需要生成
                if(!isNewSimulator){
                    taskPoolService.exec(new OssTask(task, imsi, TaskTypeEnum.ADD_TASK.getType(), apkId, DateUtil.getCurrentDateStr(), mark));
                }
                redisClient.put(CacheKey.TASK_DAY + mark + imsi, DateUtil.getCurrentDateStr(), 7200);
            } else {
                status = TaskStatusEnum.valueOf(result.intValue());
                if (status == null) {
                    status = TaskStatusEnum.NONE;
                }
            }
            LOGGER.warn("添加任务 apk:{} 省份{} 登录类型:{} imsi:{} status:{} {}", new Object[]{mark, taskVo.getProvince(), taskVo.getLoginType(), imsi, result.intValue(), status.getName()});
        } else {
            status = TaskStatusEnum.HAVE_NO_SIMULATOR;
            LOGGER.warn("添加任务 apk:{} 省份{} 登录类型:{} imsi:{} {}", new Object[]{mark, taskVo.getProvince(), taskVo.getLoginType(), imsi, status.getName()});
        }
        return status;
    }


    private boolean checkNewVersion(String sysVer) {
        String targetVersion = (String) cacheParamManager.getParamValue(CacheKey.NEW_SIMULATOR_VERSION);
        if (targetVersion != null) {
            if (VersonCompareUtil.compareSimulatorVersion(sysVer, targetVersion) >= 0) {
                return true;
            }
        }
        return false;
    }



    /**
     * getAddTaskLua:
     *
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private String getAddTaskLua() {
        StringBuilder sb = new StringBuilder();
        sb.append("local queueSize=redis.call('llen', KEYS[1]) ");
        sb.append(" if queueSize<tonumber(KEYS[4]) ");
        sb.append(" then ");
        sb.append(" local apkTotal=redis.call('hget',KEYS[6],'total') local apkExecTotal=redis.call('hget',KEYS[6],'exectotal') ");
        sb.append(" if apkTotal and apkExecTotal ");
        sb.append(" then");
        sb.append(" if tonumber(apkTotal)>tonumber(apkExecTotal) ");
        sb.append(" then");
        sb.append(" local loginTotal=redis.call('hget',KEYS[8],KEYS[9]) local loginExecTotal=redis.call('hget',KEYS[8],KEYS[10]) ");
        sb.append(" if loginTotal and loginExecTotal ");
        sb.append(" then");
        sb.append(" if tonumber(loginTotal)>tonumber(loginExecTotal) ");
        sb.append(" then ");
        sb.append(" if redis.call('setnx',KEYS[7],1)==1 ");
        sb.append(" then ");
        sb.append(" redis.call('expire',KEYS[7],15) ");
        sb.append(" redis.call('hincrby',KEYS[8],KEYS[10],1) ");
        sb.append(" redis.call('append', KEYS[3],KEYS[5]) ");
        sb.append(" redis.call('hincrby', KEYS[6],'exectotal',1) ");
        sb.append(" redis.call('lPush', KEYS[1], KEYS[2]) return 1 ");
        sb.append(" else return 3 end ");
        sb.append(" else return 4 end ");
        sb.append(" else return 7 end ");
        sb.append(" else return 5 end ");
        sb.append(" else return 8 end ");
        sb.append(" else return 2 end ");
        return sb.toString();
    }

    @Override
    public TaskStatusEnum addReserveTask(TaskVo taskVo) {
        TaskStatusEnum status = TaskStatusEnum.FAIL;
        // 判断当前队列长度在范围内，并且更新用户登录标记
        String mark = taskVo.getMark();
        String sysVer = apkSimulatorService.getVer(mark);
        Integer apkId = taskVo.getApkId();
        String imsi = taskVo.getImsi();
        // 所有城市默认为-1
        taskVo.setCity(-1);
        Integer base = taskVo.getBase();
        String hasLoginKey = null;
        if (base == null || base == ApkBaseEnum.OTHER.getType()) {
            hasLoginKey = apkId + "|" + System.currentTimeMillis() + "|";
        } else {// 和基地有关
            hasLoginKey = base + "|" + System.currentTimeMillis() + "|";
        }

        String queueKey = CacheKey.VER_TASK_QUEUE + sysVer;
        boolean isNewSimulator = checkNewVersion(sysVer);
        taskVo.setNewUser(isNewSimulator);
        if (isNewSimulator) {
            queueKey = CacheKey.NEW_SDK_QUEUE + sysVer;
        }
        String task = TaskUtil.initTaskJSON(taskVo).toJSONString();
        String[] args = new String[]{CacheKey.A_IMSI + imsi, hasLoginKey, queueKey, task};
        Long result = (Long) redisClient.eval(initReserveAddTaskLua(), ReturnType.VALUE, args);
        if (result.intValue() == TaskStatusEnum.SUCCESS.getType()) {
            status = TaskStatusEnum.SUCCESS;
            taskVo.setStatus(1);
            //生成任务文件
            if(!isNewSimulator){
                taskPoolService.exec(new OssTask(task, imsi, TaskTypeEnum.ADD_TASK.getType(), apkId, DateUtil.getCurrentDateStr(), mark));
            }
            redisClient.put(CacheKey.TASK_DAY + mark + imsi, DateUtil.getCurrentDateStr(), 3600);
            simulatorLogService.addSimulatorLog(taskVo);
        }
        LOGGER.warn("添加预装新增任务 apk {} 省份{}  imsi {} status {} {}", new Object[]{mark, taskVo.getProvince(), imsi, result.intValue(), status.getName()});
        return status;
    }

    /**
     * getAddTaskLua:
     *
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private String initReserveAddTaskLua() {
        StringBuilder sb = new StringBuilder();
        sb.append(" redis.call('append', KEYS[1],KEYS[2]) ");
        sb.append(" redis.call('lPush', KEYS[3], KEYS[4]) return 1 ");
        return sb.toString();
    }


    /**
     * getMarkTaskKey:mark任务执行以及当前需要执行的总量需要定时器处理
     *
     * @param mark
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private String initTaskRecordKey(String mark) {
        return CacheKey.A_TASK + DateUtil.getDaysOfTime() + mark;
    }


    @Override
    public void updateTaskOfCache() {
        List<ChannelApkConfig> apks = channelApkInfoService.selectAllApkInfos();

        Set<String> unUsefulApkSet = new HashSet<>();
        String unsefulApk = (String) redisClient.get(CacheKey.A_UNUSEFUL_APK);
        if (unsefulApk != null) {
            StringTokenizer tokenizer = new StringTokenizer(unsefulApk, ",", false);
            while (tokenizer.hasMoreElements()) {
                String apkId = (String) tokenizer.nextElement();
                unUsefulApkSet.add(apkId);
            }
        }

        List<String> keyAndArgs = new ArrayList<String>();
        StringBuilder stringBuilder = new StringBuilder();
        if (apks != null && apks.size() > 0) {
            for (ChannelApkConfig apkConfig : apks) {
                String isDelete = apkConfig.getIsDelete();
                // 查询当前时间正态信息
                Integer dayLimit = apkConfig.getDaylimit();
                if (apkConfig.getIsNewUserLimit() != null && apkConfig.getIsNewUserLimit() == 1) {
                    if (dayLimit > 0) {
                        dayLimit = channelLimitService.getTimeLimit(apkConfig.getMark(), dayLimit);
                    }
                }
                String key = initTaskRecordKey(apkConfig.getMark());
                Integer id = apkConfig.getId();
                if (dayLimit == null || dayLimit < 0 || (StringUtils.isNotBlank(isDelete) && "1".equals(isDelete))) {
                    // 废弃的可用量置为0
                    dayLimit = 0;
                    stringBuilder.append(id.toString()).append(",");
                }
                if(unUsefulApkSet.contains(id.toString())){
                    continue;
                }
                keyAndArgs.add(key);
                keyAndArgs.add(String.valueOf(dayLimit.intValue()));
            }
            taskLuaManager.updateTaskRecord(keyAndArgs);
        }
        redisClient.put(CacheKey.A_UNUSEFUL_APK, stringBuilder.toString());

    }


    @Override
    public Integer[] getLeftTaskNum(String[] marks) throws Exception {
        if (marks != null && marks.length > 0) {
            return taskLuaManager.getNewLeftTaskNum(Arrays.asList(marks)).toArray(new Integer[0]);
        }
        return null;
    }

    @Override
    public String[] hasDoNum(List<String> marks) {
        StringBuilder sb = new StringBuilder();
        sb.append("local result={} local j=1");
        sb.append(" for i=1,#KEYS do");
        sb.append(" local total=redis.call('hget', KEYS[i], 'total')");
        sb.append(" local exectotal=redis.call('hget', KEYS[i], 'exectotal')");
        sb.append(" result[j]=total result[j+1]=exectotal j=j+2  end return result");
        String[] sizes = null;
        if (marks != null) {
            String[] keys = new String[marks.size()];
            for (int i = 0; i < marks.size(); i++) {
                String mark = marks.get(i);
                keys[i] = initTaskRecordKey(mark);
            }
            Object retult = redisClient.eval(sb.toString(), ReturnType.MULTI, keys);
            if (retult != null) {
                List numList = (List) retult;
                sizes = new String[numList.size() / 2];
                for (int i = 0, j = 0; i < numList.size(); i = i + 2, j++) {
                    String totalStr = "0";
                    String execTotalStr = "0";
                    byte[] total = (byte[]) numList.get(i);
                    byte[] execTotal = (byte[]) numList.get(i + 1);
                    if (total != null) {
                        totalStr = new String(total);
                    }
                    if (execTotal != null) {
                        execTotalStr = new String(execTotal);
                    }
                    sizes[j] = totalStr + "|" + execTotalStr;
                }
            }
        }
        return sizes;
    }



    /**
     * 非在线用户
     */
    @Override
    public TaskStatusEnum addOfflineTask(ThirdUser thirdUser, ChannelApkConfig apkConfig) throws Exception {
        String apk = apkConfig.getMark();
        Integer loginType = thirdUser.getLoginType();
        if (!checkLoginType(apk, loginType)) {
            return TaskStatusEnum.LOGIN_PARAM_ERROR;
        }

        TaskStatusEnum status = apkManager.checkOffineCanUseApk(thirdUser.getImsi(), thirdUser.getProvince(), thirdUser.getBase(), thirdUser.getApkId());
        if (status == TaskStatusEnum.SUCCESS) {
            TaskVo taskVo = TaskUtil.initTaskVo(thirdUser, apkConfig);
            status = addTask(taskVo);
            if (status == TaskStatusEnum.SUCCESS) {
                taskVo.setStatus(1);
                simulatorLogService.addSimulatorLog(taskVo);
            }
        }

        return status;
    }

    /**
     * checkLoginType:登录方式校验
     *
     * @param apk
     * @param loginType
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private boolean checkLoginType(String apk, Integer loginType) {
        List<ApkLoginTypeConfig> loginConfigList = apkManager.getOfflineLoginTypeMap().get(apk);
        if (loginConfigList != null) {
            for (ApkLoginTypeConfig loginConfig : loginConfigList) {
                if (loginConfig.getLoginType().intValue() == loginType.intValue()) {
                    return true;
                }
            }
        }
        return false;
    }

}
